#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<assert.h>

#define OPT_NUM 64
#define NUM 1024
char lineCommend[NUM];
char *myargv[OPT_NUM];
int lastCode = 0;
int lastSig = 0;

int main()
{
    while(1){
        //写一个命令行解释器：
        //打印输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);
        
        // 获取用户输入, 输入的时候，自己会输入\n
        char* s = fgets(lineCommend, sizeof(lineCommend)-1, stdin);
        assert(s != NULL);
        (void)s;
        lineCommend[strlen(lineCommend)-1] = 0; //去掉自己输入的\n
        //printf("test: %s\n", lineCommend);
        
        // "ls -a -l -i" ->"ls" "-a" "-l" "-i"
        //  字符串切割
        
        myargv[0] = strtok(lineCommend, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)//加颜色
        {
            myargv[i++] = (char*)"--color=auto";
        }
        
        while(myargv[i++] = strtok(NULL, " "));
        
        // 如果是cd 命令，不需要创建子进程，让shell自己执行对应的命令，本质就是执行系统接口
        if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);//这样就改变了父进程即shell的路径
            continue; //直接返回即可，不需要创建子进程了
        }
        
        if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if(strcmp(myargv[1], "$?" ) == 0)
            {
                printf("%d %d\n", lastCode, lastSig);
            }
            else 
            {
                printf("%s\n", myargv[1]);
            }
            continue; 
        }

        //测试是否成功, 条件编译 在Makefile声明可以使用，不声明就不使用
#ifdef DEBUG 
        for(int j = 0; myargv[j]; j++)
        {
            printf("myargv[%d]: %s\n", j, myargv[j]);
        }
#endif   
        // 内建命令
        

        //执行命令
        pid_t id = fork();
        assert(id != 1);

        if(id == 0)
        {
           execvp(myargv[0], myargv);
           exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert(ret > 0);
        (void)ret;
        lastCode = ((status>>8) & 0xFF);
        lastSig = (status & 0x7F);
    }

    return 0;
}
